#include <stdio.h>  
#include <signal.h>
#include <iostream>
#include <sstream>

#include <zsystem/core/common/logdump.h>
#include <zsystem/core/common/md5.h>
#include <zsystem/service/httpserver/httpserver.h>
#include <zsystem/core/networking/connection.h>
#include <zsystem/chatServer/chatserver_message_id.h>
#include <google/profiler.h>
#include "webagent_config.h"

#define WEBAGENT_PROGRAME_NAME "WebAgent"

#define WEBAGENT_VERSION_MAJOR    0
#define WEBAGENT_VERSION_MINOR    0
#define WEBAGENT_VERSION_MICRO    1
#define WEBAGENT_VERSION_INT      ( WEBAGENT_VERSION_MAJOR<<16 | WEBAGENT_VERSION_MINOR<<8 | WEBAGENT_VERSION_MICRO )
#define WEBAGENT_VERSION          "0.0.1"


Z_BOOL g_exit=Z_FALSE;
void
sigint(int s) 
{
  g_exit = true;
}
#pragma warning(disable:4996)
void setLogDestination(std::string& logPath)
{
  char buffer[1024];
  sprintf(buffer, "%s%s%s_%s_", logPath.c_str(), Z_PATH_SLASH, WEBAGENT_PROGRAME_NAME, "INFO");
  google::SetLogDestination(INFO, buffer);
  sprintf(buffer, "%s%s%s_%s_", logPath.c_str(), Z_PATH_SLASH, WEBAGENT_PROGRAME_NAME, "WARNING");
  google::SetLogDestination(WARNING, buffer);
  sprintf(buffer, "%s%s%s_%s_", logPath.c_str(), Z_PATH_SLASH, WEBAGENT_PROGRAME_NAME, "ERROR");
  google::SetLogDestination(ERROR, buffer);
}
#pragma warning(default:4996)

class agentTcpClient;
struct globalVariables{
  webAgentM* agent;
  agentTcpClient* tcpClient;
};

globalVariables gv;

class agentTcpClient : public zsystem::tcpClient {
public:
  agentTcpClient(zsystem::msgReceivedListener defaultListener, const Z_I32 sendBufferSize, const Z_I32 recieveBufferSize)
  :zsystem::tcpClient(defaultListener, sendBufferSize, recieveBufferSize){
  }
protected:
  void onConnected(zsystem::tcpConnection* connection)
  {
    kxAgentData::AgentLoginRequest req;
    req.set_id(FLAGS_id);
    req.set_timestamp((Z_I32)time(Z_NULL));

    char md5string[64] = {0};
    Z_SNPRINTF(md5string, sizeof(md5string),  "%u%u%s", FLAGS_id, req.timestamp(), FLAGS_key.c_str());
    Z_I32 strlen = strnlen(md5string, 64);
    zsystem::md5 md5obj;
    Z_U8 result[16] = {0};
    md5obj.update((const Z_U8*)md5string, strlen);
    md5obj.getResult(result);
    char md5Ret[33] = {0};
    Z_SNPRINTF(md5Ret, sizeof(md5Ret), "%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x%0.2x",
             result[0], result[1],  result[2],  result[3],  result[4],  result[5],  result[6],  result[7],  
             result[8],  result[9],  result[10],  result[11], result[12],  result[13],  result[14],  result[15]); 
    req.set_sign(md5Ret);

    Z_I32 len = req.ByteSize();
    char* buffer = (char*)Z_MALLOC(len);
    if ( req.SerializeToArray(buffer, len) )
    {
      if ( !this->send(MSG_AgentLogin, buffer, len) )
      {
        Z_DUMP_LOG("send agentlogin failed!");
      }
    }
    Z_FREE(buffer);
  }
  void onDisconnected(zsystem::tcpConnection* connection)
  {
    Z_DUMP_LOG("Client disconnected!");
  }
};

void 
httpServerCallback(zsystem::Z_EV_REQ* req, Z_PTR arg)
{
#if __WIN32__
  Z_DUMP("callback( method: " << evhttp_request_get_command(req) << ", status: " << evhttp_request_get_response_code(req) << ")");
//  evhttp_send_reply(req, 200, "OK", Z_NULL);
#else
  Z_DUMP("callback( method: " << req->method << ", status: " << req->status << ")");
//  evhtp_send_reply(req, EVHTP_RES_OK);
#endif
//  zsystem::httpRequestUtil::reply(req, 200, "");
}

void 
httpClientCallback(zsystem::Z_EV_REQ* req, Z_PTR arg)
{
}

void 
tcpClientCallback(zsystem::listenerContainer* container, zsystem::tcpConnection* connection, const Z_U16 seqId, const Z_U16 msgId, char* const buffer, const Z_U16 len)
{
  Z_DUMP_LOG("tcpClientCallback( seqId = " << seqId << ", " << msgId << ")");
  switch ( msgId )
  {
  case MSG_AgentLogin:
    {
    }
    break;
  case MSG_ProxyToChatServers:
    break;
  case MSG_ProxyToPhpServers:
    break;
  }
}

int
main(int argc, char ** argv)
{
  google::InitGoogleLogging(argv[0]);

  std::string version(WEBAGENT_VERSION);
  google::SetVersionString(version);
  google::ParseCommandLineFlags(&argc, &argv, true);

  // Todo: Load config
  setLogDestination(FLAGS_LogPath);
#ifdef WIN32
  WSADATA wsaData;  
  DWORD Ret;  
  if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)  
  {
    printf("WSAStartup failed with error %d\n", Ret);  
    return -1;  
  }
  CHECK_EQ(evthread_use_windows_threads(), 0);
#else
  CHECK_EQ(evthread_use_pthreads(), 0);
#endif
  event_set_log_callback(Z_LIBEVENT_LOG_PRINT);
  event_set_mem_functions(Z_MALLOC_FUNC, Z_REALLOC_FUNC, Z_FREE_FUNC);

  memset(&gv, 0, sizeof(gv));

  // Todo: start webAgent
  Z_I32 portPos = FLAGS_BindAddress.find(':');
  if ( portPos <= 0 ) 
  {
    printf("Bind address error!\n", Ret);  
    return -1;
  }
  std::string httpServerIp = FLAGS_BindAddress.substr(0, portPos);
  int httpServerPort = atoi( FLAGS_BindAddress.substr(portPos+1).c_str() );

  portPos = FLAGS_PhpServerAddress.find(':');
  if ( portPos <= 0 ) 
  {
    printf("PHP server address error!\n", Ret);  
    return -1;
  }
  std::string phpServerIp = FLAGS_PhpServerAddress.substr(0, portPos);
  int phpServerPort = atoi( FLAGS_PhpServerAddress.substr(portPos+1).c_str() );

  gv.agent = Z_NEW(webAgentM)(httpServerIp.c_str(), httpServerPort, FLAGS_ThreadNumber, FLAGS_Backlog, 
                                      phpServerIp.c_str(), phpServerPort);
  gv.agent->setCallback("", httpServerCallback, &gv);
  gv.agent->setClientCallback(httpClientCallback, &gv);

  if ( !gv.agent->start() )
  {
    printf("Start web agent failed!\n", Ret);  
    Z_SAFE_DELETE(gv.agent, ~webAgent);
    return -1;
  }
  
  gv.tcpClient = Z_NEW(agentTcpClient) (tcpClientCallback, FLAGS_SendBufferSize, FLAGS_ReceiveBufferSize);
  if ( !gv.tcpClient->connect(FLAGS_ChatServerAddress.c_str()) )
  {
    printf("Connect chat server failed!\n", Ret);  
    gv.agent->stop();
    Z_SAFE_DELETE(gv.agent, ~webAgent);
    Z_SAFE_DELETE(gv.tcpClient, ~agentTcpClient);
    return -1;
  }
  {
    Z_DUMP("Server start running");
    signal(SIGINT, sigint);
    while(!g_exit)
    {
      zsystem::ssleep(5);
      Z_DUMP("trace log");
      google::FlushLogFiles(INFO);
    }
  }

  gv.agent->stop();
  gv.tcpClient->close();

  Z_SAFE_DELETE(gv.agent, ~webAgent);
  Z_SAFE_DELETE(gv.tcpClient, ~agentTcpClient);


#ifdef WIN32
  WSACleanup();  
#endif

  Z_DUMP_LOG("To shutdown google log and flags");
  google::ShutDownCommandLineFlags();
  Z_DUMP_LOG("Server exit success");
  google::FlushLogFiles(INFO);
  google::ShutdownGoogleLogging();

  return 0;  
}  

